"""Rule module for defining condition-action rules.

This module provides:
- Rule: A class that combines validators (conditions) and handlers (actions)
- Validator: Type alias for condition checking functions
- Handler: Type alias for action handling functions
"""

from collections.abc import Sequence
from datetime import datetime
import types
from typing import Callable, TypeAlias, ContextManager

from pydantic import BaseModel, Field

from xrule.utils.compile_util import compile_module

Validator: TypeAlias = Callable[..., bool]
Handler: TypeAlias = Callable[..., None]
RuleID: TypeAlias = str


class RuleConfig(BaseModel):
    id: RuleID = Field(description="规则ID")
    scripts: str = Field(description="脚本")
    version: int = Field(description="版本")
    expire_at: datetime = Field(description="过期时间")

    def active(self) -> bool:
        """Check if the rule is active based on the current date and time."""
        return datetime.now() < self.expire_at


class Rule:
    """A class that combines condition validators with action handlers.

    Attributes:
    ----------
    validators : list[Validator]
        List of validator functions that check conditions against facts.
    handlers : list[Handler]
        List of handler functions that execute actions when conditions are met.
    """

    # 规则的条件部分，用于匹配满足特定条件的 fact (事实)
    validators: list[Validator]
    # 规则的动作部分，用于触发匹配规则后的操作
    handlers: list[Handler]
    config: RuleConfig
    __metadata_module: types.ModuleType | None

    def __init__(
        self,
        validators: Sequence[Validator] = (),
        handlers: Sequence[Handler] = (),
        config: RuleConfig | None = None,
        module: types.ModuleType | None = None,
    ):
        self.validators = validators
        self.handlers = handlers
        self.config = config
        self.__metadata_module = module

    def __del__(self):

        del self.validators
        del self.handlers
        del self.__metadata_module


def compile_rule(config: RuleConfig) -> Rule:
    """Compile a specific rule by its ID"""
    module = compile_module(config.scripts, "rule_module")
    validators = [
        getattr(module, func_name)
        for func_name in dir(module)
        if callable(getattr(module, func_name)) and func_name.startswith("validator")
    ]
    handlers = [
        getattr(module, func_name)
        for func_name in dir(module)
        if callable(getattr(module, func_name)) and func_name.startswith("handler")
    ]
    return Rule(validators=validators, handlers=handlers, config=config)


class VersionConflictError(Exception):
    ...


class RuleRepository:
    rules: dict[RuleID, Rule]
    rule_configs: dict[RuleID, RuleConfig]
    version: int

    def __init__(self):
        self.rules = {}
        self.rule_configs = {}

    def load_rule_configs(self):
        raise NotImplementedError(
            "This method should be overridden in subclasses.")

    def reload_rule_configs(self):
        version = self.get_version()
        if self.version != version:
            self.rules.clear()
            self.rule_configs.clear()
            self.version = version
        self.load_rule_configs()

    def get_version(self):
        """Get the current version of the rule repository."""
        raise NotImplementedError()

    def get_rule(self, rule_id: RuleID) -> Rule | None:
        """Get a specific rule by its ID"""
        return self.rules.get(rule_id)

    def compile(self) -> None:
        """Compile a specific rule by its ID"""

        for rule_id, config in self.rule_configs.items():
            self.rules[rule_id] = compile_rule(config)


class RepositoryManager(ContextManager):
    """A context manager for managing the lifecycle of a RuleRepository."""

    def __init__(self, repo: RuleRepository):
        self.repo = repo

    def __enter__(self):
        self.version = self.repo.get_version()
        self.repo.load_rule_configs()
        self.repo.compile()
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        """Raise any exception triggered within the runtime context."""
        version = self.repo.get_version()
        if self.version != version:
            raise VersionConflictError(
                f"Version mismatch: {self.version} != {version}"
            )
        return None
